package org.dreamwork.cai.srf;

import org.dreamwork.cai.annotation.ADescription;
import org.dreamwork.cai.annotation.AParameter;
import org.dreamwork.cai.annotation.ARemoteExport;
import org.dreamwork.cai.tools.Misc;
import org.dreamwork.util.StringUtil;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 用于远程服务路由交换的 java Method 描述结构。
 * <p>由于java的<strong>方法重载</strong>特性，简单的字符串无法准确地描述一个 java Method</p>
 */
public class MethodInfo implements Serializable {
    public final String name;
    public final String description;
    public final String type;
    public String uuid;
    public final List<ParameterInfo> parameters = new ArrayList<> ();

    public transient Class<?> returnType;
    public transient Method method;

    public MethodInfo (Method method) {
        this (method.getName (), method);
    }

    public MethodInfo (String name, Method method) {
        this.name   = name;
        this.method = method;
        returnType  = method.getReturnType ();
        this.type   = returnType.getName ();
        this.uuid   = Misc.sha256 (method);

        String desc = null;
        if (method.isAnnotationPresent (ARemoteExport.class)) {
            ARemoteExport export = method.getAnnotation (ARemoteExport.class);
            desc = export.description ();
        }
        // 若 ARemoteExport 未提供 description 属性，尝试从 ADescription 标注中提取描述信息
        if (StringUtil.isEmpty (desc) && method.isAnnotationPresent (ADescription.class)) {
            ADescription description = method.getAnnotation (ADescription.class);
            desc = description.value ();
        }

        if (!StringUtil.isEmpty (desc)) {
            this.description = desc;
        } else {
            this.description = null;
        }

        fillParameters ();
    }

    private void fillParameters () {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations ();
        Class<?>[] parameterTypes = method.getParameterTypes ();

        for (int i = 0; i < parameterTypes.length; i ++) {
            ParameterInfo info = new ParameterInfo (parameterTypes[i]);
            fillParameterInfo (info, parameterAnnotations [i]);

            parameters.add (info);
        }
    }

    private void fillParameterInfo (ParameterInfo info, Annotation[] ans) {
        String desc = null;
        for (Annotation an : ans) {
            if (an instanceof AParameter) {
                AParameter ap = (AParameter) an;
                desc = ap.description ();
                info.setName (ap.name ());
                info.setConverter (ap.converter ());
            } else if (an instanceof ADescription) {
                if (StringUtil.isEmpty (desc)) {
                    // ADescription 的优先级比 AParameter.description 的优先级低
                    desc = ((ADescription) an).value ();
                }
            }
        }

        if (!StringUtil.isEmpty (desc)) {
            info.setDescription (desc);
        }
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) return true;
        if (o == null || getClass () != o.getClass ()) return false;
        MethodInfo that = (MethodInfo) o;
        return uuid != null && uuid.equals (that.uuid);
    }

    @Override
    public int hashCode () {
        return Objects.hash (name);
    }

    @Override
    public String toString () {
        return "MethodInfo{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                ", type='" + type + '\'' +
                ", uuid='" + uuid + '\'' +
                ", parameters=" + parameters +
                ", returnType=" + returnType +
                ", method=" + method +
                '}';
    }
}